/**
 * 填写产生式集合
 *
 * 这个函数的目的是将求出的终结符X，存放到第T条产生式的左部对应的firstVT集合中。
 * @param {*} X 终结符
 * @param {*} T 标记产生式id
 */
const setFIRSTVT = (X, T) => {
    for (let i = 0; i < 20; i++) {
        if (i === T) {
            for (let j = 0; j < 20; j++) {
                // 扫码判断是否加入
                if (X === FIRSTVT[T][j]) {
                    // 集合中已存在，不加入
                    return;
                } else if (FIRSTVT[T][j] === '\0') {
                    // 填入数组最后
                    FIRSTVT[T][j] = X;
                    break;
                }
            }
            break;
        }
    }
}

/**
 * 查找FIRSTVT集元素
 *
 * S标记产生式的位置，X为将要计算的产生式的左部。这个函数就比较复杂了，
 * 它将完成求出一个产生式左部的firstVT的终结符的重要任务，可以说是主控程序了。
 * 它的算法是逐个遍历产生式，对每个产生式求出该产生式的直接a,
 * 并且若有E→Qa…还要递归的求出Q的firstVT()将其并入firstVT(E)的集合中。
 *
 * @param {*} X 第i个产生式的第n个元素
 * @param {*} S 第i个产生式
 */
const getFIRSTVT = (X, S) => {
    let j = 0;
    let T = 0; // X position
    let L = 0; // X offspring length

    let C = [];

    // T 赋值的目的是标记当前是第几个产生式
    for (let i = 0; i < 20; i++) {
        if (INPUT[i][0] === X) {
            // 标记产生式的位置
            T = i;
        }
    }

    // 按照规则从产生式的右部第一个字符开始（形如 S-->a，第4个字符开始为产生式右部）处理，直至遇上'/n'
    // 每一次都判断指针j指向的字符若满足p-->w中w的规定则放进C[]
    // 若遇上‘|’或'\n'则求这段右部对应的firstVT()
    for (let i = 4; i < 20; i++) {
        if (INPUT[T][i] === '|' || INPUT[T][i] === '\n') {
            // 刚开始走不到这里，走到这里说明完成了产生式右部的一次扫描，然后初始化指针偏移量j，L保存右部
            // 某产生式的内容长度

            L = j; // j指针所指位置为C[]中字符串的长度
            j = 0; // 交给L后就清零，以供下一次使用

            // 遍历上一次完成右部扫码的结果，并将相应的终结符存储到FIRSTVT集
            for (let k = 0; k < L; k++) {
                // 要是数组C[]中的终结符，如小写字母'a'~'z'，加减乘除，乘方，#
                // 则归入fisrstVT集中
                // 若是Aab...则a成为F()一部分，b被忽略,A也不用求first集？？？需要求！！！除非A==X
                // 若是QRa...，则不是算符优先文法，故不可能
                // 若是a...则只是填进a

                // ascII码 97: a   122: z
                // 也就是小写字母集合 + 一些运算符号
                if (IsVT(C[k])) {
                    // 只能用一次,因是算符优先文法，故前两个中必会至少存在一个终结符
                    // 也就是说 索引为 0 | 1，必须为终结符（代码优化，索引超过3报错）
                    setFIRSTVT(C[k], S); // 存入FIRSTVT中，S标记产生式的位置
                    break;
                }
            }
            // 存储完成

            // 判断产生式是否为 Qa... 形式，如果是，则递归
            // ascII码 65: A   90: Z
            const c0 = C[0];
            const c0Code = c0.charCodeAt();
            if (c0Code >= 65 && c0Code <= 90 && c0 !== X) {
                // 若C[0]中为A~Z,并且C[0]不是X(否则将无限循环)，则递归的进行填充
                // C[0]不是X，也就是C[0]不能是产生式左部，否则就无限循环了 例如：S-->Sa
                let flag = 0;

                // 查找 Qa... Q对应的产生式，递归求FirstVT
                for (let count = 0; count < 20; count++) {
                    if (INPUT[count][0] === c0) {
                        flag = 1; // 找到，然后标记找到
                    }
                    if (flag === 1) {
                        // 递归
                        getFIRSTVT(c0, S); // S为子集中的元素填入父集中指明了方向
                        break; // 产生式左部不应该重复，我觉得可以break，原文没有break
                    }
                }
            }
        } else if (INPUT[T][i] !== '!' && INPUT[T][i] !== '\0') {
            C[j] = INPUT[T][i]; // j从0开始
            j++;
        }

        if (INPUT[T][i] === '\n') {
            // 该行结束
            break;
        }
    }
}

/**
 * 写入 LASTVT集
 * @param {*} X 终结符
 * @param {*} T 标记产生式id
 */
const setLASTVT = (X, T) => {
    for (let i = 0; i < 20; i++) {
        if (i === T) {
            for (let j = 0; j < 20; j++) {
                if (X === LASTVT[i][j]) {
                    // 若集合中已存在，则不写入
                    break;
                }
                if (LASTVT[i][j] === '\0') {
                    LASTVT[i][j] = X; // 写入数组最后
                    return;
                }
            }
            break;
        }
    }
}

/**
 * 查找LASTVT集元素
 *
 * @param {*} X
 * @param {*} S
 */
const getLASTVT = (X, S) => {
    let j = 0;
    let T = 0; // X position
    let L = 0; // X offspring length
    let C = [];

    for (let i = 0; i < 20; i++) {
        if (INPUT[i][0] === X) { // 找到要处理的产生式
            T = i; // 标记产生式的位置
            break;
        }
    }

    // 按照规则从产生式的右部第一个字符开始处理，直至遇上'/n'
    // 每一次都判断指针j指向的字符若满足p-->w中w的规定则放进C[]
    // 若遇上‘|’或'\n'则求这段右部对应的LASTVT()
    for (let i = 4; i < 20; i++) {
        if (INPUT[T][i] === '|' || INPUT[T][i] === '\n') {
            // 刚开始走不到这里
            L = j; // j指针所指位置为C[]中字符串的长度
            j = 0; // 交给L后就清零，以供下一次使用
            for (let k = L - 1; k >= 0; k--) {
                // 要是数组C[]中的终结符，如小写字母'a'~'z'，加减乘除，乘方，#
                // 则归入LASTVT集中
                // 若是Aab...则a成为F()一部分，b被忽略,A也不用求first集？？？需要求！！！除非A==X
                // 若是QRa...，则不是算符优先文法，故不可能
                // 若是a...则只是填进a
                if (IsVT(C[k])) {
                    // 只能用一次,因是算符优先文法，故前两个中必会至少存在一个终结符
                    setLASTVT(C[k], S); // 存入LASTVT中，S标记产生式的位置
                    break; // 跳出循环保证存入的是最左边的终结符
                }
            }

            const clM1 = C[L - 1];
            const clM1Code = clM1.charCodeAt();
            if (clM1Code >= 65 && clM1Code <= 90 && clM1 !== X) {
                // 若C[0]中为A~Z,并且C[]中没有终结符，则递归的进行填充
                let flag = 0;
                for (let count = 0; count < 20; count++) {
                    if (INPUT[count][0] === clM1) { // 找到将要处理的产生式
                        flag = 1;
                    }

                    if (flag === 1) {
                        getLASTVT(clM1, S); // S为子集中的元素填入父集中指明了方向
                    }
                }
            }
        } else if (INPUT[T][i] != '|' && INPUT[T][i] != '\0') { // 该行没结束，过滤'\0'
            C[j] = INPUT[T][i]; // j从0开始
            j++;                // 移进
        }

        if (INPUT[T][i] == '\n') {
            // 该行结束
            break;
        }
    }
}

const setINPUT = () => {
    console.log('---- setINPUT ----');
    const cin = document.getElementById('cin');
    const inputLineEle = document.getElementById('input_line');

    cin.value = cin.value + '\n';

    const line = parseInt(inputLineEle.value);

    // 设置 第 inputLineEle.value 行
    for (let i = 0; i < 20; i++) {
        INPUT[line][i] = cin.value[i];

        if (cin.value[i] === '\n')
            break;
    }

    inputLineEle.value = `${line + 1}`;
    cin.value = '';
}

const allOk = () => {
    console.log('---- allOk ----');
    const inputLineEle = document.getElementById('input_line');
    const line = parseInt(inputLineEle.value);

    INPUT[line][0] = '\n';
}

const sArr = [
    'S-->v=E|E?|l\n',
    'E-->E+T|E-T|T\n',
    'T-->T*F|T/F|F\n',
    'F-->(E)|v|c\n',
    '\n',
];

const setTestData = () => {
    for (let i = 0; i < sArr.length; i++) {
        for (let j = 0; j < 20; j++) {
            INPUT[i][j] = sArr[i][j];

            if (sArr[i][j] === '\n')
                break;
        }
    }
}

const displayGS = () => {
    const DisplayGS_ELE = document.createElement('pre');
    let gs = 'G(S):\n';
    for (let i = 0; i < sArr.length; i++) {
        gs += `${sArr[i]}`;
    }
    DisplayGS_ELE.innerText = gs;
    document.getElementById('DisplayGS').appendChild(DisplayGS_ELE);
}

/**
 * 显示FirstVT和LastVT
 */
const DisplayFirstVT_LasVT = () => {
    console.log('---INPUT: ', INPUT);

    // 从输入集合INPUT中读取，读到结束符，读取每行保存FIRSTVT集
    for (let i = 0; INPUT[i][0] !== '\n'; i++) {
         // 对所有的产生式
        getFIRSTVT(INPUT[i][0], i); // 第i个产生式，求fistvt()，核心算法
        //              ↑       ↑
        //        第i个产生式的  第i个产生式
        //        第一个元素开始
        //     （也就是产生式左部）
    }

    // printf("FIRSTVT SET:\n");

    const DisplayFirstVT_ELE = document.createElement('pre');
    let firstVTSet = 'firstVT:\n';
    for (let i = 0; INPUT[i][0] !== '\n'; i++) {
        // 对所有产生式，进行输出fistvt集，最大处理能力20个产生式
        firstVTSet += `FIRSTVT(${INPUT[i][0]})={`;
        for (let j = 0; FIRSTVT[i][j] !== '\0'; j++) {
            firstVTSet += `${FIRSTVT[i][j]}`;
            if (FIRSTVT[i][j + 1] !== '\0') {
                firstVTSet += `,`;
            }
        }
        firstVTSet += `}\n`;
    }
    DisplayFirstVT_ELE.innerText = firstVTSet;
    document.getElementById('DisplayFirstVT').appendChild(DisplayFirstVT_ELE);


    for (let i = 0; INPUT[i][0] !== '\n'; i++) {
        // 对所有的产生式
        getLASTVT(INPUT[i][0], i); // 第i+1个产生式，求lastvt()，核心算法
    }
    const DisplayLastVT_ELE = document.createElement('pre');
    let lastVTSet = 'lastVT:\n';
    for (let i = 0; INPUT[i][0] !== '\n'; i++) {
        lastVTSet += `LASTVT(${INPUT[i][0]})={`;
        for (let j = 0; LASTVT[i][j] !== '\0'; j++) {
            // 逐次输出
            lastVTSet += `${LASTVT[i][j]}`;
            if (LASTVT[i][j + 1] !== '\0') {
                lastVTSet += `,`;
            }
        }
        lastVTSet += `}\n`;
    }
    DisplayLastVT_ELE.innerText = lastVTSet;
    document.getElementById('DisplayLastVT').appendChild(DisplayLastVT_ELE);
}
